import { nextTick } from "vue"

// --------------------------- 本地浏览器数据库缓存 --------------------------- //

/** TODO 还没有实现单列模式 */
class IndexedDB {
  private DB_NAME: string
  private DB_VERSION: number
  private DB_STORE_NAME: string
  private db: IDBDatabase | null | undefined

  constructor() {
    this.DB_NAME = "shekou_DB"
    this.DB_VERSION = 1.0
    this.DB_STORE_NAME = "3d_tiles"
    this.db = null
  }

  async get(url: string, onProgress: (progress: number) => void) {
    this.db = await this.initDB()
    if (!this.db) return
    /** 创建一个事务 并指定操作的对象存储空间*/
    const transaction = this.db.transaction(this.DB_STORE_NAME, "readwrite")
    /** 选择指定的对象空间 */
    const store = transaction.objectStore(this.DB_STORE_NAME)
    /**  */
    const getRequest = store.get(url)
    return new Promise((resolve, reject) => {
      getRequest.onsuccess = async (e: Dictionary) => {
        const tilesFiles = e.target.result
        try {
          console.log(tilesFiles, "是否有缓存")
          // 假如已经有缓存了 直接用缓存
          if (tilesFiles) {
            if (onProgress) {
              onProgress(100)
            }
            resolve(tilesFiles.blob)
          } else {
            // 假如没有缓存 请求新的模型存入
            const blob = await this.put(url, onProgress)
            resolve(blob)
          }
        } catch (error) {
          reject()
        }
      }

      getRequest.onerror = (e) => {
        console.log("error", e)
        reject()
      }
    })
  }

  private async put(url: string, onProgress: (progress: number) => void) {
    const response = await fetch(url)
    if (response.status !== 200) {
      throw new Error("Request failed")
    }

    const contentLength = response.headers.get("Content-Length")
    const totalBytes = parseInt(contentLength || "", 10)
    let downloadedBytes = 0

    const readableStream = response.body

    const { readable, writable } = new TransformStream()

    const writer = writable.getWriter()

    const reader = readableStream?.getReader()

    const pump = async () => {
      if (!reader) return
      const { done, value } = await reader.read()

      if (done) {
        writer.close()
        return
      }

      writer.write(value)

      downloadedBytes += value.length

      if (onProgress) {
        const progress = (downloadedBytes / totalBytes) * 100
        onProgress(+progress.toFixed(2))
      }

      return pump()
    }

    await pump()

    let blob: Blob | null | ArrayBuffer = null
    try {
      blob = await new Response(readable).arrayBuffer()
    } catch (e) {
      console.log("请求arrayBuffer失败,用blob方式")
      blob = await new Response(readable).blob()
    }

    const obj: Dictionary = {
      ssn: url
    }
    obj.blob = new Blob([blob])

    if (!this.db) return
    const inputRequest = this.db.transaction([this.DB_STORE_NAME], "readwrite").objectStore(this.DB_STORE_NAME).add(obj)

    return new Promise((resolve, reject) => {
      inputRequest.onsuccess = function () {
        console.log("glb数据添加成功")
        resolve(obj.blob)
      }
      inputRequest.onerror = function (evt) {
        console.log("glb数据添加失败", evt)
        reject()
      }
    })
  }

  private initDB() {
    if (!window.indexedDB) {
      console.error("您的浏览器不支持本地数据库，您可以考虑更换其他浏览器")
      return null
    } else {
      /** 打开或创建数据库*/
      const request = window.indexedDB.open(this.DB_NAME, this.DB_VERSION)

      return new Promise<IDBDatabase | undefined>((resolve, reject) => {
        request.onsuccess = (e: Event) => {
          resolve((e.target as IDBOpenDBRequest).result as IDBDatabase)
        }

        request.onerror = (e: Dictionary) => {
          console.error("数据库打开错误", e.target?.errorCode)
          reject()
        }
        /** 第一次打开或者版本更新 */
        request.onupgradeneeded = (e: IDBVersionChangeEvent) => {
          const db = (e.target as IDBOpenDBRequest).result as IDBDatabase
          console.log("数据库升级", db)

          // 创建一个对象存储空间（类似数据表）
          const objectStore = db.createObjectStore(this.DB_STORE_NAME, { keyPath: "ssn" })

          // 创建索引（可选）
          objectStore.createIndex("name", "name", { unique: false })
        }
      })
    }
  }
}

// --------------------------- localStorage缓存 --------------------------- //

/** TODO 还没实现单列模式 */
class Storage {
  _prefixKey: string
  _prefixUser: string
  _prefixState: string
  constructor(prefixkey: string = "Shekou_", prefixUser: string = "user_", _prefixState: string = "state_") {
    this._prefixKey = prefixkey
    this._prefixUser = prefixUser
    this._prefixState = _prefixState
  }

  private _parseData(key: string, data: string | null) {
    try {
      const value = data ? JSON.parse(data) : null
      if (!value) {
        return null
      }
      if (value.time) {
        const now = new Date().getTime()
        if (Number(value.time) < now) {
          localStorage.removeItem(key)
          return null
        }
        return value.value
      }
      return value.value
    } catch (error) {
      return null
    }
  }

  /**
   * @param key
   * @param data
   * @param time 设置过期时间(单位为分钟)，如 10 => 10分钟
   */
  private _set(key: string, data: any, time = 0): void {
    const H = time * 1e3 * 60
    const now = new Date().getTime() + H
    const val = JSON.stringify({ time: time ? String(now) : null, value: data })
    localStorage.setItem(key, val)
  }

  private _get<T = any>(key: string): T | null {
    const data = localStorage.getItem(key)
    return this._parseData(key, data)
  }

  private _remove(key: string): void {
    localStorage.removeItem(key)
  }

  private _clear() {
    localStorage.clear()
  }

  /** 设置业务缓存 */
  set(key: string, data: any, time = 0) {
    this._set(this._prefixKey + key, data, time)
  }

  get<T = any>(key: string) {
    return this._get<T>(this._prefixKey + key)
  }

  remove(key: string) {
    this._remove(this._prefixKey + key)
  }

  clear() {
    this._clear()
  }

  /** 设置用户数据缓存 */
  setUser(key: string, data: any, time = 0) {
    this._set(this._prefixUser + key, data, time)
  }

  getUser<T = any>(key: string) {
    return this._get<T>(this._prefixUser + key)
  }

  removeUser(key: string) {
    this._remove(this._prefixUser + key)
  }

  /** 设置通讯数据缓存 */
  getState<T = any>(key: string) {
    const _data = this._get<T>(this._prefixState + key)

    if (_data) {
      nextTick(() => {
        this.removeState(this._prefixState + key)
      })
    }

    return _data
  }

  setState(key: string, data?: any) {
    this._set(this._prefixState + key, data || true)
  }

  removeState(key: string) {
    this._remove(key)
  }
}

const db = new IndexedDB()
const cache = new Storage()

/** 用于记录是否需要刷新数据 */
const setComplete = cache.setState("complete")
const getComplete = cache.getState("complete")

export { cache, db, setComplete, getComplete }
